package com.ldr.custom.spring.v3.aop.support;

import com.ldr.custom.spring.v3.aop.config.GPAopConfig;
import com.ldr.custom.spring.v3.aop.intercept.GPAfterReturningAdviceInterceptor;
import com.ldr.custom.spring.v3.aop.intercept.GPAfterThrowingAdviceInterceptor;
import com.ldr.custom.spring.v3.aop.intercept.GPMethodBeforeAdviceInterceptor;
import lombok.Data;

import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Data
public class GPAdvisedSupport {

    private Class<?> targetClass;

    private Object targetSource;
    //存储每个方法对应的aop调用链
    private Map<Method, List<Object>> methodCache=new HashMap<>();

    private GPAopConfig gpAopConfig;

    private Pattern pointCutClassPattern;

    public GPAdvisedSupport(GPAopConfig gpAopConfig) {
        this.gpAopConfig = gpAopConfig;
    }


    public void setTargetClass(Class<?> targetClass) {
        this.targetClass = targetClass;
        parse();
    }

    //解析切面
    private void parse() {
        String pointCut = gpAopConfig.getPointCut()
                .replaceAll("\\.", "\\\\.")
                .replaceAll("\\\\.\\*", ".*")
                .replaceAll("\\(", "\\\\(")
                .replaceAll("\\)", "\\\\)");

        String pointCutForClassRegex = pointCut.substring(0, pointCut.lastIndexOf("\\(") - 4);
        pointCutClassPattern = Pattern.compile("class " + pointCutForClassRegex.substring(
                pointCutForClassRegex.lastIndexOf(" ") + 1));

        try {
            //切面类
            Class<?> aspectClazz = Class.forName(this.gpAopConfig.getAspectClass());
            Map<String, Method> aspectMethods = new HashMap<>();
            for (Method method : aspectClazz.getMethods()) {
                aspectMethods.put(method.getName(), method);
            }


            Pattern pattern = Pattern.compile(pointCut);
            for (Method method : targetClass.getMethods()) {

                String methodString = method.toString();
                if (methodString.contains("throws")) {
                    methodString = methodString.substring(0, methodString.lastIndexOf("throws")).trim();
                }

                Matcher matcher = pattern.matcher(methodString);
                if (matcher.matches()) {
                    List<Object> advices = new LinkedList<>();
                    //把每个方法包装成MethodInterceptor
                    Object aspect = aspectClazz.newInstance();
                    //before
                    if (!(null == gpAopConfig.getAspectBefore() || "".equals(gpAopConfig.getAspectBefore()))) {

                        advices.add(new GPMethodBeforeAdviceInterceptor(aspectMethods.get(gpAopConfig.getAspectBefore()), aspect));
                    }
                    //after
                    if (!(null == gpAopConfig.getAspectAfter() || "".equals(gpAopConfig.getAspectAfter()))) {
                        advices.add(new GPAfterReturningAdviceInterceptor(aspectMethods.get(gpAopConfig.getAspectAfter()), aspect));
                    }
                    //afterThrowing
                    if (!(null == gpAopConfig.getAspectAfterThrow() || "".equals(gpAopConfig.getAspectAfterThrow()))) {

                        GPAfterThrowingAdviceInterceptor throwingAdviceInterceptor = new GPAfterThrowingAdviceInterceptor(aspectMethods.get(gpAopConfig.getAspectAfterThrow()), aspect);
                        throwingAdviceInterceptor.setThrowName(gpAopConfig.getAspectAfterThrowName());
                        advices.add(throwingAdviceInterceptor);
                    }
                    methodCache.put(method, advices);
                }


            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) throws NoSuchMethodException {
        //targetClass是实现接口类型
        List<Object> chain = methodCache.get(method);
        if (chain==null) {
            //如果没有使用aop代理，直接调用自己的方法
            Method m = targetClass.getMethod(method.getName(), method.getParameterTypes());
            chain = methodCache.get(m);
            methodCache.put(m,chain);
        }

        return chain;
    }

    //匹配切面表达式
    public boolean pointCutMatch() {
        return this.pointCutClassPattern.matcher(this.targetClass.toString()).matches();
    }
}
